﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Threading;
using System.IO;
using System.IO.Compression;
using System.Diagnostics;

namespace AndroidRTLPatcher
{

    public partial class frmMain : Form
    {
        cszip zipAction = new cszip();
        public frmMain()
        {
            InitializeComponent();
        }

        #region Core functions
        public string ADBShell(string adbInput, int waitTimeOut)
        {
            try
            {
                //Empty values
                string result = string.Empty;
                string error = string.Empty;
                string output = string.Empty;
                writeToLog("Sending to ADB: " + adbInput);
                System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo(toolPath + "adb.exe");
                procStartInfo.Arguments = adbInput;
                procStartInfo.RedirectStandardOutput = true;
                procStartInfo.RedirectStandardError = true;
                procStartInfo.UseShellExecute = false;
                procStartInfo.CreateNoWindow = true;
                procStartInfo.WorkingDirectory = toolPath;
                System.Diagnostics.Process proc = new System.Diagnostics.Process();
                proc.StartInfo = procStartInfo;

                ////Create output
                proc.ErrorDataReceived += (object sender, DataReceivedEventArgs args) =>
                {
                    if (args.Data != null)
                    {
                        error += args.Data + "\n";
                    }
                };
                proc.OutputDataReceived += (object sender, DataReceivedEventArgs args) =>
                {
                    if (args.Data != null)
                    {
                        result += args.Data + "\n";
                    }
                };

                proc.Start();
                proc.BeginOutputReadLine();
                proc.BeginErrorReadLine();
                if (waitTimeOut == 0) { proc.WaitForExit(); } else { proc.WaitForExit(waitTimeOut); } //Timeout if prompted
                output = result + error;
                
              writeToLog("ADB Returned: " + output);
                if (output.Contains("adb server is out of date"))
                {
                    DialogResult msgResult = MessageBox.Show("Looks like HTC Sync is active!\nPlease shut it down and try again!", "HTC Sync Active!", MessageBoxButtons.RetryCancel, MessageBoxIcon.Hand);
                    if (msgResult == DialogResult.Cancel)
                    {
                        raiseCancel = true;
                        writeToLog("User Cancelled!");
                        throw new Exception("User cancelled!");
                    }
                    else if (msgResult == System.Windows.Forms.DialogResult.Retry)
                    {
                        result = ADBShell(adbInput,0); //Recurse
                    }
                }
                proc.Close();
                proc.Dispose();
                return output;
            }
            catch (Exception objException)
            {
                throw objException;
            }
        } //A function that returns the ADB result for any command input into it. Future implementation will use Managed ADB code.
        private void JAVAShell(string javaInput)
        {
            try
            {
                writeToLog("Sending to JAVA: " + javaInput);
                System.Diagnostics.ProcessStartInfo procStartInfo2 = new System.Diagnostics.ProcessStartInfo("java.exe", javaInput);
                procStartInfo2.UseShellExecute = false;
                procStartInfo2.RedirectStandardOutput = true;
                procStartInfo2.RedirectStandardError = true;
                procStartInfo2.CreateNoWindow = true;
                procStartInfo2.WorkingDirectory = toolPath;
                System.Diagnostics.Process proc2 = new System.Diagnostics.Process();
                proc2.StartInfo = procStartInfo2;
                proc2.Start();
                proc2.WaitForExit();
                string result = proc2.StandardOutput.ReadToEnd();
                result = result + "\n" + proc2.StandardError.ReadToEnd(); //Capture errors
                if (String.IsNullOrWhiteSpace(result))
                {
                    writeToLog("Java operation complete!");
                }
                else
                {
                    writeToLog("JAVA Returned: \n" + result);
                }
            }
            catch (Exception objException)
            {
                throw objException;
            }
        } //A function that returns the ADB result for any command input into it. Future implementation will use Managed ADB code.
        public void FindAndKillProcess(string name)
        {
            try
            {
                writeToLog("Attempting to kill " + name);
                System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("taskkill.exe");
                procStartInfo.Arguments = "/f /IM " + name;
                procStartInfo.RedirectStandardOutput = true;
                procStartInfo.RedirectStandardError = true;
                procStartInfo.UseShellExecute = false;
                procStartInfo.CreateNoWindow = true;
                procStartInfo.WorkingDirectory = Environment.SystemDirectory;
                System.Diagnostics.Process proc = new System.Diagnostics.Process();
                proc.StartInfo = procStartInfo;
                proc.Start();
                // Get the output into a string
                proc.WaitForExit();
                string result = proc.StandardOutput.ReadToEnd();
                result = result + "\n" + proc.StandardError.ReadToEnd(); //Some outputs are this instead
                writeToLog("KILL command returned: \n" + result);
                if (result.Contains("not found"))
                {
                    writeToLog("Process already killed.");
                    return;
                }
                else if (result.Contains("Access denied!"))
                {
                    writeToLog("Can not kill process: Access Denied!");
                    return;
                }

            }
            catch (Exception objException)
            {
                writeToLog("ERROR: " + objException.Message);
                return;
            }
        } //Kill a process!!!
        public void writeToLog(string logEntry)
        {
            try
            
            {
                //Prepare Loggeer             
                TextWriter logWriter = new StreamWriter(logPath, true);
                DateTime currentTime = new DateTime();
                currentTime = DateTime.Now;

                //Check if new run or current run
                if (logFirst == true)
                {
                    logFirst = false;
                    logWriter.WriteLine("-------------------------------------------");
                }
                //Write to log
                logWriter.WriteLine(currentTime.ToString() + ": " + logEntry);
                logWriter.Close();
            }
            catch (Exception ex)
            {
                if (ex.Message.Contains("The process cannot access the file") == false) //If writing to log fails for some reason... What can you do, really? Write it to the log?
                {
                    throw ex;
                }
            }
        } // Log Writer
        public void errorHandler(Exception ex) //Handles exceptions
        {
            MessageBox.Show("An unexpected error occured and has been logged into the log file:\n" + ex.Message);
            writeToLog("ERROR OCCURED: " + ex.Message);
            writeToLog("ERROR DETAILS: " + ex.Data);
        }
        public string GetProperty(string name)
        {
            try
            {
                string bpNew = ADBShell("shell getprop " + name,0);              
                if (bpNew == null)
                {
                    return "Not Detected!";
                }
                else
                {
                    return bpNew.Trim();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        } //Get a ROM property
        public string checkDevice() //Checks if a device is connected and in what mode
        {
            writeToLog("Checking connection");
            string checkDevice = ADBShell("devices",0);
            //Parse devices - regardless of type
            string[] deviceLines = Regex.Split(checkDevice.ToLower(), "\n");
            if (deviceLines.Length > 3 && deviceLines[1].Contains("device")) //If there is a device detected
            {
                writeToLog("Detected device!");
                return "device";
            }
            else if (deviceLines.Length > 3 && deviceLines[1].Contains("recovery")) //If the device is in Recovery
            {
                writeToLog("Detected device in recovery");
                return "recovery";
            }
            else if (deviceLines.Length < 4) //No devices detected
            {
                writeToLog("No device detected!");
                return "none";
            }
            else
            {
                writeToLog("Device Detection error! ADB devices returned: " + checkDevice);
                return "error";
            }
        }
        #endregion

        #region Form Functions       
        private string SenseDetection()
        {
         // Build Autodetection //
            writeToLog("Detecting SENSE Version");
            string senseVersion = GetProperty("ro.htc.common.version");
            if (senseVersion == "" || senseVersion == null)
            {
                writeToLog("Autodetect failed!");
                return "failed";
            }
            else if (senseVersion.Substring(0,3) == "2.0") //Autodetected Sense 2.0
            {
                //Sense 3.0 over-ride - for some reason many Sense 3.0 roms use "2.0" in the build.prop
                if(currentBuild.AndroidVersion.Substring(0,3) == "2.3")
                {
                    //Assuming all Gingerbread with Sense are Sense 2.1+
                    writeToLog("Autodetected Sense 3.0!"); //At the moment, Sense 2.1 and Sense 3.0 are considered the same
                    return "2.1";
                }
                writeToLog("Autodetected Sense 2.0!");
                return "2.0";
            }
            else if (senseVersion.Substring(0, 3) == "2.1") //Autodetect Sense 2.1
            {
                writeToLog("Autodetected Sense 2.1!");
                return "2.1";
            }
            else if (senseVersion.Substring(0,2) == "1.")
            {
                writeToLog("Detected Sense 1.X!");
                return "1.X";
            }
            else if (senseVersion.Substring(0, 2) == "3.")
            {                
                writeToLog("Autodetected Sense 3.0!");
                return "2.1";
            }
            else //Autodetect failed!
            {
                writeToLog("Autodetect failed!");
                return "failed";
            }
        }
        private void CompatAutoDetect()
        {
            string listFiles = ADBShell("shell ls /system/framework/",0);
            writeToLog("Checking Compatibility");
            if (listFiles.Contains("ls: /system/framework/: No such file or directory") && isRecovery)
            {
                //Mounting system!
                writeToLog("Mounting system directory...");
                lblCompat.ForeColor = Color.Purple;
                lblCompat.Text = "Mounting system...";
                ADBShell("shell mount system",0);
                ADBShell("shell mount sd-ext",0);
                lblStatus.Text = "Testing...";
                listFiles = ADBShell("shell ls /system/framework/",0);
            }
            if (listFiles.Contains("odex")) //If the device is ODEX based
            {
                lblCompat.ForeColor = Color.Red;
                lblCompat.Text = "ODEX based - not compatible!";
            }
            else if (listFiles.Contains(".jar") & !listFiles.Contains(".odex"))
            {
                lblCompat.ForeColor = Color.Green;
                lblCompat.Text = "JAR based, build compatible!";
                grpBuildType.Enabled = true;
                currentBuild.Compatible = true;
            }
            else
            {
                MessageBox.Show("Could not test compatibility.", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                writeToLog("ERROR OCCURED: Could not test compatibility!");
                return;
            }
            writeToLog(lblCompat.Text);

            // Build Autodetection //
            string fileNames = ADBShell("shell ls /system/framework/",0);
            Boolean sensePresent = fileNames.Contains("com.htc.framework.jar");
            string mainSense;
            if (!sensePresent)
            {
                mainSense = "nexus";
            }
            else
            {
                mainSense = SenseDetection();
            }
            switch (mainSense)
            {
                case "nexus": //Autodetected No Sense
                    rdoNexus.Checked = true;
                    lblAutoDetect.Visible = true;
                    break;

                case "2.0": //Autodetected Sense 2.0                  
                    rdoSenseHD.Checked = true;
                    lblAutoDetect.Visible = true;
                    break;

                case "2.1": //Autodetect Sense 2.1
                    rdoSenseGB.Checked = true;
                    lblAutoDetect.Visible = true;
                    break;

                case "1.X": //Autodetect Sense 1.X
                    rdoSense.Checked = true;
                    lblAutoDetect.Visible = true;
                    break;

                case "failed":
                    lblAutoDetect.Visible = false;
                    break;
            }
            
        } //Test Build Compatibility
        private void ResetForm() //Reset to the beginning
        {
            lblCompat.ForeColor = Color.Black;
            lblStatus.ForeColor = Color.Black;
            lblCompat.Text = "";
            lblConnection.Text = "";
            lblBase.Text = "";
            lblBuildName.Text = "";
            lblVersion.Text = "";
            grpConnection.Enabled = true;
            grpBuildType.Enabled = false;
            grpGo.Enabled = false;
            grpDist.Enabled = false;
            rdoNexus.Checked = false;
            rdoSense.Checked = false;
            rdoSenseHD.Checked = false;
            chkPush.Checked = true;
            chkZip.Checked = false;
            chkDisplay.Checked = false;
            lblAutoDetect.Visible = false;
            chkKeyboard.Enabled = false;
            chkLibPatcher.Enabled = false;
            chkKeyboard.Checked = false;
            writeToLog("Form Reset.");
        }
        private void btnReboot_Click(object sender, EventArgs e)
        {
            DialogResult dlgResult = MessageBox.Show("Are you sure you want to reboot your phone?", "Reboot phone?", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if (dlgResult == DialogResult.Yes)
            {
                writeToLog("Sending reboot to device");
                updateStatus("Rebooting device...");
                disableForProcessing(false);
                ADBShell("reboot",0);
                enableAfterProcessing();
                MessageBox.Show("Your phone has just been rebooted.", "Operation Complete!", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        private void btnRecovery_Click(object sender, EventArgs e)
        {
            DialogResult dlgResult = MessageBox.Show("Are you sure you want to reboot your phone to recovery?\nNote that not all phones support this method!", "Reboot phone?", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if (dlgResult == DialogResult.Yes)
            {
                writeToLog("Sending reboot recovery to device");
                updateStatus("Rebooting device...");
                disableForProcessing(false);
                ADBShell("reboot recovery",0);
                enableAfterProcessing();
                MessageBox.Show("Your phone has just been rebooted.", "Operation Complete!", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }
        #endregion

        #region Interface Functions
        //Buttons//
        private void btnTestConnection_Click(object sender, EventArgs e)
        {
            try
            {
                ResetForm();
                this.Enabled = false;
                this.UseWaitCursor = true;
                string isDevice = checkDevice();
                if (isDevice == "device") //If there is a device detected
                {
                    isRecovery = false;
                    lblConnection.ForeColor = Color.Green;
                    lblConnection.Text = "Connection successful!";
                }
                else if (isDevice == "recovery") //If the device is in Recovery
                {
                    isRecovery = true;
                    lblConnection.ForeColor = Color.Purple;
                    lblConnection.Text = "Recovery Mode!";
                    MessageBox.Show("Warning, your device is in revoery mode!\nIf you have just flashed a new room, boot it at least once before patching.\nOtherwise, continue as usual.", "Warning!", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
                else if (isDevice == "none") //No devices detected
                {
                    lblConnection.ForeColor = Color.Red;
                    lblConnection.Text = "No device detected!";
                    grpBuildType.Enabled = false;
                    grpGo.Enabled = false;
                    this.Enabled = true;
                    this.UseWaitCursor = false;
                    return;
                }
                else
                { 
                    lblConnection.ForeColor = Color.Red;
                    lblConnection.Text = "Detection Error!";
                    grpBuildType.Enabled = false;
                    grpGo.Enabled = false;
                    MessageBox.Show("Could not detect connection status!\n Check log for more details.", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    writeToLog("ERROR OCCURED: Could not test connection status!\n");
                    this.Enabled = true;
                    this.UseWaitCursor = false;
                    return;
                }
                PropertyDetector();
                CompatAutoDetect();
                chkKeyboard.Enabled = true;
                chkLibPatcher.Enabled = true;
                grpDist.Enabled = true;
                chkPush.Enabled = true;
                grpGo.Enabled = true;
                this.Enabled = true;
                this.UseWaitCursor = false;
            }
            catch (Exception objException)
            {
                writeToLog("ERROR: " + objException.Message);
                MessageBox.Show("An error occured procuring your build information!\nPlease send your log file and build.prop (if available) to the developer!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                this.Enabled = true;
                this.UseWaitCursor = false;
            }
        }
        //Test Device       
        private void btnExit_Click(object sender, EventArgs e)
        {
            this.Close();
            this.Dispose();
        }
        private void btnGo_Click(object sender, EventArgs e)
        {
            //Check and set build type
            if (rdoNexus.Checked)
            {
                textSource = "files\\text_generic\\";
            }
            else if (rdoSense.Checked)
            {
                textSource = "files\\text_sense\\";
            }
            else if (rdoSenseHD.Checked)
            {
                textSource = "files\\text_senseHD\\";
            }
            else if (rdoSenseGB.Checked)
            {
                textSource = "files\\text_senseGB\\";
            }
            else
            {
                writeToLog("No build type selected, and somehow GO was pressed.");
                DialogResult newDLG = MessageBox.Show("Warning, no build type selected. Default type will be set as Nexus. Continue?", "Warning!", MessageBoxButtons.YesNo);
                if (newDLG == System.Windows.Forms.DialogResult.Yes)
                {
                    textSource = "files\\text_generic\\";
                }
                else
                {
                    writeToLog("Process Aborted!");
                    return;
                }
            }
            string filePath = null;
            if (chkZip.Checked)
            {
                //In case of ZIP, select zip first//
                SaveFileDialog filePathDlg = new SaveFileDialog();
                filePathDlg.Filter = "*.zip|*.zip";
                filePathDlg.Title = "Select ROM Zip file";
                filePathDlg.OverwritePrompt = true;
                DialogResult dlgResult = filePathDlg.ShowDialog();
                if (DialogResult == System.Windows.Forms.DialogResult.Cancel)
                {
                    writeToLog("Aborted File Selection!");
                    MessageBox.Show("Operation aborted!", "Operation Aborted!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                    return;
                }
                filePath = filePathDlg.FileName;
            }
            writeToLog("Text source set to: " + textSource);
            backgroundWorker.RunWorkerAsync(filePath);
            disableForProcessing(true);
        }//Starts the patch building process by passing all values to the patch maker window//

        //Radio Buttons and Options//

        private void chkPush_CheckedChanged(object sender, EventArgs e)
        {
            if (chkPush.Checked == false && chkDisplay.Checked == false && chkZip.Checked == false)
            {
                grpGo.Enabled = false;
            }
            else
            {
                grpGo.Enabled = true;
            }
        }
        private void chkZip_CheckedChanged(object sender, EventArgs e)
        {
            if (chkPush.Checked == false && chkDisplay.Checked == false && chkZip.Checked == false)
            {
                grpGo.Enabled = false;
            }
            else
            {
                grpGo.Enabled = true;
            }
        }
        private void chkDisplay_CheckedChanged(object sender, EventArgs e)
        {
            if (chkPush.Checked == false && chkDisplay.Checked == false && chkZip.Checked == false)
            {
                grpGo.Enabled = false;
            }
            else
            {
                grpGo.Enabled = true;
            }
        }
        private void btnAbout_Click(object sender, EventArgs e)
        {
            frmAbout newAbout = new frmAbout();
            newAbout.ShowDialog();
        }

        //Main Forms//
        private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            FindAndKillProcess("adb.exe");
            backgroundSplash.Dispose();
            backgroundWorker.Dispose();
            writeToLog("Closed successfuly");
            this.Dispose();
        }
        private void frmMain_Load(object sender, EventArgs e)
        {
            writeToLog("Starting program...");
            writeToLog(Application.ProductName + " " + Application.ProductVersion);
            this.Hide();
            splashScreen.cylonEye1.Style = ProgressBarStyle.Blocks;
            splashScreen.cylonEye1.ForeColor = Color.Red;
            splashScreen.cylonEye1.Maximum = 100;
            splashScreen.cylonEye1.Value = 0;
            backgroundSplash.RunWorkerAsync();
            splashScreen.ShowDialog();
            writeToLog("Program Initialized!");
        }
        private void backgroundSplash_DoWork(object sender, DoWorkEventArgs e)
        {
            splashScreen.cylonEye1.Value = 0;
            splashScreen.lblSplash.Text = "Preparing...";
            FindAndKillProcess("adb.exe");
            splashScreen.cylonEye1.Value = 25;
            splashScreen.lblSplash.Text = "Killing existing ADB connections...";
            ADBShell("kill-server",0);
            splashScreen.cylonEye1.Value = 50;
            splashScreen.lblSplash.Text = "Restarting ADB Server...";
            ADBShell("start-server",10000);
            splashScreen.cylonEye1.Value = 75;
            splashScreen.lblSplash.Text = "Remounting System...";
            ADBShell("remount",0); //innitialize
            splashScreen.cylonEye1.Value = 100;
        }
        private void backgroundSplash_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            splashScreen.Dispose();
            this.Show();
        }
        #endregion

        #region Processing Interfaces
        private void disableForProcessing(bool useDialog) //Disables current window and shows status window
        {
            waitWindow.StartPosition = FormStartPosition.CenterScreen;
            waitWindow.frmMain = this;
            if (useDialog == true)
            {
                waitWindow.ShowDialog(this);
            }
            else
            {
                this.Enabled = false;
                this.UseWaitCursor = true;
                waitWindow.progressMain.Visible = false;
                waitWindow.Show();
            }
        }
        private void enableAfterProcessing() //Hides status window and enables main
        {
                waitWindow.Hide();
                this.Enabled = true;
                this.UseWaitCursor = false;
        }
        public void updateStatus(string status) //Updates status window
        {
            if (raiseCancel == true)
            {
                backgroundWorker.CancelAsync();
            }
            if (backgroundWorker.CancellationPending == true)
            {
                DialogResult dlgCancel = MessageBox.Show("Are you sure you want to cancel?", "Cancel?", MessageBoxButtons.YesNo);
                if (dlgCancel == System.Windows.Forms.DialogResult.Yes)
                {
                    writeToLog("Operation cancelled!");
                    throw new Exception("Operation cancelled!");
                }
            }
            waitWindow.lblStatusMsg.Text = status;
            waitWindow.progressMain.Style = ProgressBarStyle.Marquee;
            waitWindow.progressMain.MarqueeAnimationSpeed = 100;
            waitWindow.progressMain.Maximum = 100;
            waitWindow.progressMain.Value = 0;
            waitWindow.Refresh();
        }
        private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            enableAfterProcessing();
        }
        private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            patcherMain((string)e.Argument);
        }
        private void PropertyDetector()
        {
            try
            {
                currentBuild.AndroidVersion = GetProperty("ro.build.version.release").Substring(0, 3);
                currentBuild.BuildName = GetProperty("ro.product.version"); //Alternative to build name
                if (currentBuild.BuildName == "" || currentBuild.BuildName == null)
                {
                    currentBuild.BuildName = GetProperty("ro.build.description"); //Old method, no longer used - used as fallback
                }
                currentBuild.BuildBase = GetProperty("ro.product.model"); //Which device it was built from
                if (currentBuild.AndroidVersion == "Not")
                {
                    lblVersion.Text = "Not Detected!";
                }
                else if (currentBuild.AndroidVersion == "2.2")
                {
                    lblVersion.Text = "2.2 (Froyo)";
                }
                else if (currentBuild.AndroidVersion == "2.3")
                {
                    lblVersion.Text = "2.3 (Gingerbread)";
                }
                else if (currentBuild.AndroidVersion == "3.0")
                {
                    lblVersion.Text = "3.0 (Honeycomb)";
                }
                lblBase.Text = currentBuild.BuildBase;
                lblBuildName.Text = currentBuild.BuildName;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }//Gets properties of the build and puts in a list of properties.
        #endregion

        #region Patcher functions
        private void preCleanUp(bool recurse) //Pre cleanup - make sure no previous patches exist in output folder//
        {
            try
            {
                if (File.Exists(toolPath + "framework.jar"))
                {
                    File.Delete(toolPath + "framework.jar");
                    writeToLog("Deleted " + toolPath + "framework.jar");
                }
                if (Directory.Exists(toolPath + "classes"))
                {
                    Directory.Delete(toolPath + "classes", true);
                    writeToLog("Deleted " + toolPath + "classes");
                }
                if (File.Exists(toolPath + "classes.dex"))
                {
                    File.Delete(toolPath + "classes.dex");
                    writeToLog("Deleted " + toolPath + "classes.dex");
                }
                if (File.Exists(toolPath + "libwebcore_fixed.so"))
                {
                    File.Delete(toolPath + "libwebcore_fixed.so");
                    writeToLog("Deleted " + toolPath + "libwebcore_fixed.so");
                }
                if (File.Exists(toolPath + "libandroid_runtime_fixed.so"))
                {
                    File.Delete(toolPath + "libandroid_runtime_fixed.so");
                    writeToLog("Deleted " + toolPath + "libandroid_runtime_fixed.so");
                }
                if (File.Exists(toolPath + "libwebcore.so"))
                {
                    File.Delete(toolPath + "libwebcore.so");
                    writeToLog("Deleted " + toolPath + "libwebcore.so");
                }
                if (File.Exists(toolPath + "libandroid_runtime.so"))
                {
                    File.Delete(toolPath + "libandroid_runtime.so");
                    writeToLog("Deleted " + toolPath + "libandroid_runtime.so");
                }
            }
            catch (Exception ex)
            {
                if (ex.Message.Contains("not empty"))
                {
                    preCleanUp(true);//recurse
                }
                else
                {
                    throw ex;
                }
            }
            finally
            {
                if (File.Exists(toolPath + "framework.jar") || (Directory.Exists(toolPath + "classes")) || (File.Exists(toolPath + "classes.dex")) && recurse == true)
                {
                    preCleanUp(true);
                }
            }
        }
        public void adbPull(string sourcePath, string targetPath)           //Retreive a file
        {
            updateStatus("Fetching " + sourcePath + "...");
            writeToLog("Pulling file " + sourcePath);
            string pullString = ADBShell("pull " + sourcePath + @" """ + targetPath.Replace(@"\", @"/") + @"""",0);
            if (!pullString.Contains("KB/s")) //Did not transfer!
            {
                if (pullString.Contains("remote object '/system/build.prop' does not exist")) //Needs to mount system
                {
                    ADBShell("shell mount system",0);
                    ADBShell("shell mount sd-ext",0);
                    adbPull(sourcePath, targetPath);
                }
                throw new Exception("File transfer failed!\n Error was: " + pullString);
            }
        }
        public bool adbPush(string sourcePath, string targetPath)           //Push a file
        {
        PUSH:
            //Push
            writeToLog("Pushing " + sourcePath + " to " + targetPath);
            string newString = ADBShell(@"push """ + sourcePath.Replace(@"\", @"/") + @""" " + targetPath,0);
            if (!newString.Contains("KB/s") || newString.Contains("failed to copy")) //Did not transfer!
            {
                writeToLog("ERROR: File transfer to phone failed!\nOutput was: " + newString);
                DialogResult CancelRetry = MessageBox.Show("An error occured trying to transfer the file to the phone!\nThe phone system may be in use, or may not be rooted.\n If your phone has prompted you for root rights, please approve them and retry. Also, if possible, reboot into recovery and retry.\n\n(Tip: If you choose to cancel, you will be given the option to save the patch manually so you can try to apply it yourself using the ProTools).", "Writing to phone failed!", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
                if (CancelRetry == System.Windows.Forms.DialogResult.Retry)
                {
                    writeToLog("Retrying...");
                    goto PUSH;
                }
                else
                {
                    return false;
                }
            } //IF = Transfer failed
            else //Transfer successfull
            {
                return true;
            }
        }
        public void extractFromJar(string sourcePath, string targetPath)        //Extract classes.dex
        {
            updateStatus("Extracting classes.dex from JAR...");
            writeToLog("Extracting classes.dex from JAR");
            zipAction.FastZipExtract(sourcePath, targetPath, "classes.dex");
        }
        public void extractClasses(string sourceFile, string targetPath)        //Extract classes dir from classes.dex
        {
            //Extract the classes dir using JAVA
            updateStatus("Extracting classes files from classes.dex (this might take a while)");
            writeToLog("Extracting classes dir and files from classes.dex");
            JAVAShell(@"-jar baksmali-1.2.6.jar -o """ + targetPath + @""" """ + sourceFile + @"""");
        }
        public void compressClasses(string sourcePath, string targetFile)       //
        {
            //Recompress using Java
            updateStatus("Recompressing classes files... (this might take a while)");
            writeToLog("Recompressing classes files");
            JAVAShell(@"-Xmx512M -jar smali-1.2.6.jar """ + sourcePath + @""" -o """ + targetFile + @"""");
        }
        private void mountSystem()
        {
            writeToLog("Remounding system as writeable");
            ADBShell("shell mount -o remount,rw device system",0);
            ADBShell("shell mount -o remount,rw device sd-ext",0);
        }
        private void patcherMain(string zipPath)
        {
            try
            {
                updateStatus("Preparing...");
                //Cleanup files before execution
                preCleanUp(true);

                //Pull the framework file
                adbPull("/system/framework/framework.jar", toolPath + "framework.jar"); 
                
                //If checked, pull relevent library files
                if (chkLibPatcher.Checked)
                {
                    adbPull("/system/lib/libwebcore.so", toolPath + "libwebcore.so");
                    adbPull("/system/lib/libandroid_runtime.so", toolPath + "libandroid_runtime.so");
                }

                extractFromJar(toolPath + "framework.jar", toolPath);  //Extract classes.dex
                extractClasses("classes.dex", "classes");

                //Copy relevent graphics file
                writeToLog("Starting copy operations on Graphics dir!");
                string[] graphicList = Directory.GetFiles(toolPath + "files\\graphics\\");
                for (int i = 0; i < graphicList.Length; i++)
                {
                    if (!File.Exists(graphicList[i]))
                    {
                        throw new Exception("File " + graphicList[i] + " does not exist!");
                    }
                    File.Copy(graphicList[i], toolPath + "classes\\android\\graphics\\" + Path.GetFileName(graphicList[i]), true);
                    writeToLog("Copying " + graphicList[i] + " to " + toolPath + "classes\\android\\graphics\\" + Path.GetFileName(graphicList[i]));
                    updateStatus("Creating Patch: Graphics Directory...");
                }

                //copy relevent text files
                writeToLog("Starting copy operations on Texts dir!");
                string[] textList = Directory.GetFiles(toolPath + textSource);
                for (int i = 0; i < textList.Length; i++)
                {
                    File.Copy(textList[i], toolPath + "classes\\android\\text\\" + Path.GetFileName(textList[i]), true);
                    writeToLog("Copying " + textList[i] + " to " + toolPath + "classes\\android\\text\\" + Path.GetFileName(textList[i]));
                    updateStatus("Creating Patch: Texts Directory...");
                }
                writeToLog("Copy operation complete!");
                compressClasses("classes", "classes_new.dex");

                //Replace original classes.dex framework.jar
                updateStatus("Saving classes file back to JAR");
                writeToLog("Saving classes file back to JAR...");
                zipAction.FastZipSave(toolPath + "framework.jar", toolPath + "classes_new.dex", "classes.dex");

                //Patch Libraries
                bool patchLibs = false;
                if (chkLibPatcher.Checked)
                {
                    updateStatus("Patching Core Libraries...");
                    writeToLog("Patching Core Libraries.");
                    patchLibs = libPatcherMain();
                    if (!patchLibs)
                    {
                        writeToLog("Libraries not patched, either not successful or not required");
                        MessageBox.Show("The core library files were not patched!\nMost probably they were already patched and therefor skipped (more details can be found in the program log).", "Core Library files not patched!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    }
                }
                
                //Push back to device
                if (chkPush.Checked == true) 
                {

                //PUSH:
                    mountSystem();
                    updateStatus("Patching device...");
                    bool pushFW = adbPush(toolPath + "framework.jar", "/system/framework/framework.jar");
                    if (!pushFW)
                    {
                        DialogResult NewYesNo = MessageBox.Show("Do you wish to save a copy of the patch on your computer?\n\n(Tip: You can apply the patch manually to your phone or your ROM Zip using the ProTools section!)", "Save a copy of the patch?", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                        if (NewYesNo == System.Windows.Forms.DialogResult.Yes)
                        {
                            enableAfterProcessing();
                            SaveFileDialog savePatch = new SaveFileDialog();
                            savePatch.OverwritePrompt = true;
                            savePatch.Filter = "framework.jar|*.jar";
                            DialogResult dlgSavePatch = savePatch.ShowDialog(this);
                            if (dlgSavePatch == System.Windows.Forms.DialogResult.Cancel)
                            {
                                writeToLog("Aborted File Selection!");
                                MessageBox.Show("Operation aborted!", "Operation Aborted!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                                disableForProcessing(true);
                                goto CREATEZIP;
                            }
                            else
                            {
                                File.Copy(toolPath + "framework.jar", savePatch.FileName);
                                DialogResult dlgYEsNo = MessageBox.Show("File saved successfuly.\nDo you want to view the file now?", "File saved!", MessageBoxButtons.YesNo);
                                if (dlgYEsNo == DialogResult.Yes)
                                {
                                    string argument = @"/select, " + savePatch.FileName;
                                    System.Diagnostics.Process.Start("explorer.exe", argument);
                                    disableForProcessing(true);
                                    goto CREATEZIP;
                                }
                            }
                        } //IF = Failed to copy, chose to save manually
                        else //Selected "NO" to save manually
                        {
                            writeToLog("Aborted File Saved!");
                            MessageBox.Show("Operation aborted!", "Operation Aborted!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                            disableForProcessing(true);
                            goto CREATEZIP;
                        } //IF = Chose not to save manually
                    } //IF = Pushing file failed

                    //Push Libraries if patched
                    if (chkLibPatcher.Checked && patchLibs)
                    {
                        adbPush(toolPath + "libwebcore_fixed.so", "/system/lib/libwebcore.so");
                        adbPush(toolPath + "libandroid_runtime_fixed.so", "/system/lib/libandroid_runtime.so");
                    }

              } //IF = Checked 'push'

                //Optional - Install Keyboard
                if (chkKeyboard.Checked == true && chkPush.Checked == true)
                {
                    KeyboardInstall();
                }

                CREATEZIP:
                //Create ZIP
                if (chkZip.Checked == true)
                {
                    //Copy template ZIP to target location
                    updateStatus("Creating ZIP....");
                    File.Copy(toolPath + "zip\\rtlpatch.zip", zipPath,  true);
                    
                    updateStatus("Integrating patch to flashable zip...");
                    zipAction.FastZipSave(zipPath, toolPath + "framework.jar", "\\system\\framework\\framework.jar");
                    if (chkKeyboard.Checked) //Integrate keyboard patch if selected
                    {
                        zipAction.FastZipSave(zipPath, toolPath + "GBKB\\app\\LatinIME.apk", "\\system\\app\\LatimIME.apk");
                        zipAction.FastZipSave(zipPath, toolPath + "GBKB\\lib\\libjni_latinime.so", "\\system\\lib\\libjni_latinime.so");
                    }
                    if (chkLibPatcher.Checked && patchLibs) //Integrate patched libraries if selected
                    {
                        zipAction.FastZipSave(zipPath, toolPath + "libandroid_runtime_fixed.so", "\\system\\lib\\libandroid_runtime.so");
                        zipAction.FastZipSave(zipPath, toolPath + "libwebcore_fixed.so", "\\system\\lib\\libwebcore.so");
                    }
                    DialogResult dlgResult = MessageBox.Show("ZIP Created successfuly. Do you want to attempt to push it to the SD card?\nSelecting 'No' will just show you the file.", "ZIP Complete!", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                    if (dlgResult == DialogResult.Yes)
                    {
                        writeToLog("Sending ZIP to SD Card");
                        updateStatus("Sending ZIP to SD Card");
                        string newString = ADBShell(@"push """ + zipPath.Replace(@"\", @"/") + @""" /sdcard/rtlpatch.zip",0);
                        if (!newString.Contains("KB/s") || newString.Contains("failed to copy")) //Did not transfer!
                        {
                            updateStatus("Failed to transfer, showing file instead!");
                            DialogResult dlgYEsNo = MessageBox.Show("ZIP file created successfuly.\nHowever, it could be transfered to your SD Card.\nDo you want to locate the file manually instead?", "Transfer Failed", MessageBoxButtons.YesNo, MessageBoxIcon.Error);
                            if (dlgYEsNo == DialogResult.Yes)
                            {
                                string argument = @"/select, " + zipPath;
                                System.Diagnostics.Process.Start("explorer.exe", argument);
                            }
                        }
                        else
                        {
                            MessageBox.Show("ZIP file created successfuly, and is now at the root of your SD Card", "Operation Complete!", MessageBoxButtons.OK, MessageBoxIcon.Information);
                        }                       
                    }
                    else if (dlgResult == DialogResult.No)
                    {
                        string filePath = toolPath + "zip\\rtlpatch.zip";
                        string argument = @"/select, " + zipPath;
                        System.Diagnostics.Process.Start("explorer.exe", argument);
                    }
                }
                //Ask to reboot
                if (chkPush.Checked == true)
                {
                    DialogResult dlgResult = MessageBox.Show("Patching Operation Complete! \nHowever, before the changes take effect, you MUST reboot your device. \nDo you wish to do so now?", "Operation Complete!", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                    if (dlgResult == DialogResult.Yes)
                    {
                        writeToLog("Sending reboot to device");
                        updateStatus("Rebooting phone...");
                        ADBShell("reboot",0);
                        MessageBox.Show("Your phone has just been rebooted. \nFirst boot after the patching may take longer than usual. This is normal.\nThank you for using the Android RTL Patcher!", "Operation Complete!", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                }
                //Show file if asked
                if (chkDisplay.Checked == true)
                {
                    string filePath = toolPath + "framework.jar";
                    string argument = @"/select, " + filePath;
                    System.Diagnostics.Process.Start("explorer.exe", argument);
                }
            }
            catch (Exception newException)
            {
                if (newException.Message.Contains("Operation cancelled!"))
                {
                    MessageBox.Show("Operation Cancelled!", "Cancelled!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                else if (newException.Message.Contains("No such file or directory"))
                {
                    MessageBox.Show("File transfer failed because some crucial program files are missing.\n Please re-download the program and make sure to extract everything before trying again.", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    writeToLog("ERROR: " + newException.Message);
                }
                else
                {
                    MessageBox.Show("An unexpected error occured and has been logged.\n" + newException.Message, "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    writeToLog("ERROR: " + newException.Message);
                }
            }
            finally
            {
                FindAndKillProcess("adb.exe");
                FindAndKillProcess("java.exe");
            }
        }
        private void KeyboardInstall()
        {
            updateStatus("Installing Gingerbread Keyboard...");
            writeToLog("Installing Gingerbreak Keyboard...");
            string kbPush1 = ADBShell(@"push """ + toolPath.Replace(@"\", @"/") + @"GBKB/app"" /system/app",0);
            if (!kbPush1.Contains("KB/s") || kbPush1.Contains("failed to copy")) //Did not transfer!
            {
                throw new Exception("File transfer to phone failed!\nOutput was: " + kbPush1);
            }
            string kbPush2 = ADBShell(@"push """ + toolPath.Replace(@"\", @"/") + @"GBKB/lib"" /system/lib",0);
            if (!kbPush2.Contains("KB/s") || kbPush2.Contains("failed to copy")) //Did not transfer!
            {
                throw new Exception("File transfer to phone failed!\nOutput was: " + kbPush2);
            }
            writeToLog("Installed keyboard successfuly!");
        }

        #endregion

        #region Public Values
        string textSource;
        public string toolPath = Path.GetDirectoryName(Application.ExecutablePath) + "\\Toolkit\\";
        public string logPath = Path.GetDirectoryName(Application.ExecutablePath) + "\\log.txt";
        bool logFirst = true;
        volatile frmPatcher waitWindow = new frmPatcher();
        bool isRecovery;
        public buildProperties currentBuild = new buildProperties();
        frmQuery pathQuery = new frmQuery();
        frmSplash splashScreen = new frmSplash();
        #endregion

        #region External handles

        public bool raiseCancel
        {
            get { return backgroundWorker.CancellationPending; }
            set { backgroundWorker.CancelAsync(); }
        }

        #endregion

        #region ProTools

        #region Offline Operations

        #region Interface
    
        private void rdoOfflineZip_CheckedChanged(object sender, EventArgs e)
        {
            clearOfflineForm(); //First, clear form.          
            btnBrowse.Enabled = true;
            if (rdoOfflineZip.Checked)
            {               
                selectFile("zip", true);
                if (File.Exists(filePath))
                {
                    zipAutoDetect();
                }                
            }
        }
        private void rdoOfflineJar_CheckedChanged(object sender, EventArgs e)
        {
            clearOfflineForm(); //First, clear form. 
            btnBrowse.Enabled = true;
            if (rdoOfflineJar.Checked)
            {
                selectFile("jar", true);                
            }
        }

        private void btnBrowse_Click(object sender, EventArgs e)
        {
            btnPatchPro.Enabled = false;
            if (rdoOfflineJar.Checked == true)
            {
                selectFile("jar", false);           
            }
            else if (rdoOfflineZip.Checked == true)
            {
                selectFile("zip", false);
                zipAutoDetect();
            }
        }
        private void btnZipAutoDetect_Click(object sender, EventArgs e)
        {
            zipAutoDetect();
        }
        private void btnPatchPro_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Just so you know, your original files will not be affected\nInstead, new files will be created in their original directories\nnamed 'PATCHED-yourfile'", "For your information...", MessageBoxButtons.OK, MessageBoxIcon.Information);
            OfflinePatcherStep1();
        }

        private void rdoZipNone_CheckedChanged(object sender, EventArgs e)
        {
            if (File.Exists(filePath))
            {
                btnPatchPro.Enabled = true;
            }
        }
        private void rdoZipSense1_CheckedChanged(object sender, EventArgs e)
        {
            if (File.Exists(filePath))
            {
                btnPatchPro.Enabled = true;
            }
        }
        private void rdoZipSense2_CheckedChanged(object sender, EventArgs e)
        {
            if (File.Exists(filePath))
            {
                btnPatchPro.Enabled = true;
            }
        }
        private void rdoZipSense3_CheckedChanged(object sender, EventArgs e)
        {
            if (File.Exists(filePath))
            {
                btnPatchPro.Enabled = true;
            }
        }

        private void backgroundOffline_DoWork(object sender, DoWorkEventArgs e)
        {
            OfflinePatcherStep2();
        }
        private void clearOfflineForm()
        {            
            foreach (Control panelControl in panelSenseVersion.Controls) //Clear sense version buttons
                if (panelControl is RadioButton)
                {
                    ((RadioButton)panelControl).Checked = false;
                }
            filePath = null;
            txtFileName.Clear();
            panelSenseVersion.Enabled = false;
            btnZipAutoDetect.Enabled = false;
            btnPatchPro.Enabled = false;
            lblZipAutodetected.Visible = false;              
        } //Clears the Offline Operations form and its values.

        #endregion

        #region Public Valus
        string filePath;
        string OfflineTextSource;
        public buildProperties ZIPBuild = new buildProperties();
        #endregion

        #region General Functions        
        private string zipSenseDetection(bool sensePresent, string fileName) //Detects if a certain ZIP has Sense in it, and if so, returns version
        {
            try
            {
                writeToLog("ProTools: Detecting SENSE Version");
                getBuildPropZip(); //Extract build properties
                string senseVersion = ZIPBuild.SenseVersion;
                if (!sensePresent)
                {
                    writeToLog("Autodetected Nexus");
                    return "nexus";
                }
                else if (sensePresent && senseVersion == null)
                {
                    if (ZIPBuild.AndroidVersion.Substring(0, 3) == "2.3")
                    {
                        //Assuming all Gingerbread with Sense are Sense 2.1+
                        writeToLog("Autodetected Sense 3!");
                        return "2.1"; //At the moment, 2.1 and 3.0 are the considered the same
                    }
                    else
                    {
                        //While Sense is present, there is no way to determine version
                        return "failed";
                    }
                }
                else if (senseVersion.Substring(0, 3) == "2.0") //Autodetected Sense 2.0
                {
                    //Sense 3.0 over-ride - for some reason many Sense 3.0 roms use "2.0" in the build.prop
                    if (ZIPBuild.AndroidVersion.Substring(0, 3) == "2.3")
                    {
                        //Assuming all Gingerbread with Sense are Sense 2.1+
                        writeToLog("Autodetected Sense 3!");
                        return "2.1"; //At the moment, 2.1 and 3.0 are the considered the same
                    }
                    writeToLog("Autodetected Sense 2.0!");
                    return "2.0";
                }
                else if (senseVersion.Substring(0, 3) == "2.1") //Autodetect Sense 2.1
                {
                    writeToLog("Autodetected Sense 2.1!");
                    return "2.1";
                }
                else if (sensePresent && senseVersion.Substring(0, 2) == "1.")
                {
                    writeToLog("Detected Sense 1.X!");
                    return "1.X";
                }
                else if (sensePresent && senseVersion.Substring(0, 2) == "3.") //Autodetect Sense 3.0
                {
                    writeToLog("Autodetected Sense 3.0!");
                    return "2.1";
                }
                else //Autodetect failed!
                {
                    writeToLog("Autodetect failed!");
                    return "failed";
                }
            }
            catch (Exception ex)
            {
                writeToLog("ERROR: " + ex.Message);
                return "failed";
            }
        }
        private void selectFile(string fileType, Boolean auto)
        {
            OpenFileDialog filePathDlg = new OpenFileDialog();
            if (fileType == "zip")
            {
                btnZipAutoDetect.Enabled = true;
                filePathDlg.Filter = "*.zip|*.zip";
                filePathDlg.Title = "Select ROM Zip file";
            }
            if (fileType == "jar")
            {
                filePathDlg.Filter = "framework.jar|*.jar";
                filePathDlg.Title = "Select framework.jar file";
            }
            DialogResult dlgResult = filePathDlg.ShowDialog();
            if (dlgResult == System.Windows.Forms.DialogResult.Cancel)
            {
                writeToLog("Aborted File Selection!");
                if (auto == true)
                {
                    MessageBox.Show("Operation aborted!", "Operation Aborted!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                    rdoOfflineJar.Checked = false;
                    rdoOfflineZip.Checked = false;
                    clearOfflineForm();
                }
                return;
            }
            panelSenseVersion.Enabled = true;
            filePath = filePathDlg.FileName;
            txtFileName.Text = filePathDlg.FileName;
        } //Select file to process using offline tools (filetype should be either "jar" or "zip").
        private void zipAutoDetect()
        {
            if (!File.Exists(filePath)) //Make sure ZIP file path is correct
            {
                MessageBox.Show("Selected file does not exist anymore!", "File does not exist!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            if (zipAction.findFile(filePath, "build.prop") == null) //Confirm that ROM file is valid
            {
                writeToLog("build.prop not detected within Zip file");
                MessageBox.Show("The file you have selected is NOT a proper ROM file!", "File is not a ROM file", MessageBoxButtons.OK, MessageBoxIcon.Error);
                clearOfflineForm();
                return;
            }
            string mainSense = zipSenseDetection(zipAction.findFile(filePath, "com.htc.framework.jar") != null, filePath);
            switch (mainSense)
            {
                case "nexus":
                    rdoOfflineSenseNone.Checked = true;
                    lblZipAutodetected.Visible = true;
                    break;

                case "2.0": //Autodetected Sense 2.0                  
                    rdoOfflineSense2.Checked = true;
                    lblZipAutodetected.Visible = true;
                    break;

                case "2.1": //Autodetect Sense 2.1
                    rdoOfflineSense3.Checked = true;
                    lblZipAutodetected.Visible = true;
                    break;

                case "1.X":
                    rdoOfflineSense1.Checked = true;
                    lblZipAutodetected.Visible = true;
                    break;

                case "failed":
                    lblZipAutodetected.Visible = false;
                    {
                        MessageBox.Show("Sense Auto-Detect failed!\nAre you sure the ZIP file is a valid ROM?", "Auto Detection Failed!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    break;
            }
        } //Auto-detects SENSE version from a ROM Zip file
        private void getBuildPropZip() //Extracts the properties of a ZIP Rom from  its 'build.prop' file.
        {
            try
            {
                zipAction.manualZipExtract(filePath, toolPath, "build.prop", true);
                StreamReader propReader = new StreamReader(toolPath + "build.prop");
                do
                {
                    string thisLine = propReader.ReadLine().Trim();
                    if (thisLine.Length > 1 && thisLine.Substring(0, 1) != "#") //Handle escape characters
                    {
                        string[] propLine = thisLine.Split('=');
                        if (propLine[0].Contains("ro.htc.common.version"))
                        {
                            ZIPBuild.SenseVersion = propLine[1].Trim();
                        }
                        if (propLine[0].Contains("ro.build.version.release"))
                        {
                            ZIPBuild.AndroidVersion = propLine[1].Trim();
                        }
                    }
                }
                while (propReader.EndOfStream == false);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region Patcher Functions
        private void OfflinePatcherStep1()
        {
            try
            {              
                preCleanUp(true);       //Cleanup files before execution
                writeToLog("Beginning Offline Patching. File: " + filePath);

                //First, ensure file exists before patching
                if (!File.Exists(filePath)) //Make sure file path is correct
                {
                    MessageBox.Show("Selected file does not exist anymore!", "File does not exist!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    writeToLog("File no longer exists!");
                    return;
                }

              //Check and set build type
                if (rdoOfflineSenseNone.Checked)
                {
                    OfflineTextSource = "files\\text_generic\\";
                }
                else if (rdoOfflineSense1.Checked)
                {
                    OfflineTextSource = "files\\text_sense\\";
                }
                else if (rdoOfflineSense2.Checked)
                {
                    OfflineTextSource = "files\\text_senseHD\\";
                }
                else if (rdoOfflineSense3.Checked)
                {
                    OfflineTextSource = "files\\text_senseGB\\";
                }
                else //Just in case - fallback
                {
                    writeToLog("No build type selected OR detected, how did we get here?");
                    DialogResult newDLG = MessageBox.Show("Warning, no build type selected. Default type will be set as Nexus. Continue?", "Warning!", MessageBoxButtons.YesNo);
                    if (newDLG == System.Windows.Forms.DialogResult.Yes)
                    {
                        OfflineTextSource = "files\\text_generic\\";
                    }
                    else
                    {
                        writeToLog("Process Aborted!");
                        return;
                    }
                }
                writeToLog("Text source set to: " + textSource);
                backgroundOffline.RunWorkerAsync();
                disableForProcessing(true);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        private void OfflinePatcherStep2()
        {
            try
            {
                updateStatus("Preparing...");
                string fwPath = null;
                if (rdoOfflineZip.Checked) //If file is ZIP, extract framework.jar to working directory first
                {
                    writeToLog("Selected file is Zip, extracting Framework.jar");
                    fwPath = zipAction.findFile(filePath,"framework.jar");
                    if (fwPath == null)
                    {
                        throw new Exception("Framework.jar not found in file");
                    }
                    zipAction.manualZipExtract(filePath, toolPath, "framework.jar",true);
                    if (File.Exists(toolPath + "framework.jar") == false)
                    {
                        throw new Exception("Framework.jar extraction failed");
                    }
                }
                else if (rdoOfflineJar.Checked) //If file is already jar, just copy it to the working directory
                {
                    File.Copy(filePath, toolPath + "framework.jar");
                    if (File.Exists(toolPath + "framework.jar") == false)
                    {
                        MessageBox.Show("Failed to copy selected file.", "Failed to operate!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        writeToLog("Could not copy framework.jar to working directory!");
                        return;
                    }
                }
                
                extractFromJar(toolPath + "framework.jar", toolPath);  //Extract classes.dex
                extractClasses("classes.dex", "classes");

                //Copy relevent graphics file
                writeToLog("Starting copy operations on Graphics dir!");
                string[] graphicList = Directory.GetFiles(toolPath + "files\\graphics\\");
                for (int i = 0; i < graphicList.Length; i++)
                {
                    if (!File.Exists(graphicList[i]))
                    {
                        throw new Exception("File " + graphicList[i] + " does not exist!");
                    }
                    File.Copy(graphicList[i], toolPath + "classes\\android\\graphics\\" + Path.GetFileName(graphicList[i]), true);
                    writeToLog("Copying " + graphicList[i] + " to " + toolPath + "classes\\android\\graphics\\" + Path.GetFileName(graphicList[i]));
                    updateStatus("Creating Patch: Graphics Directory...");
                }

                //copy relevent text files
                writeToLog("Starting copy operations on Texts dir!");
                string[] textList = Directory.GetFiles(toolPath + OfflineTextSource);
                for (int i = 0; i < textList.Length; i++)
                {
                    File.Copy(textList[i], toolPath + "classes\\android\\text\\" + Path.GetFileName(textList[i]), true);
                    writeToLog("Copying " + textList[i] + " to " + toolPath + "classes\\android\\text\\" + Path.GetFileName(textList[i]));
                    updateStatus("Creating Patch: Texts Directory...");
                }
                writeToLog("Copy operation complete!");
                compressClasses("classes", "classes_new.dex");

                //Replace original classes.dex framework.jar
                updateStatus("Saving classes file back to JAR");
                writeToLog("Saving classes file back to JAR...");
                zipAction.FastZipSave(toolPath + "framework.jar", toolPath + "classes_new.dex", "classes.dex");
                
                string newFilePath = "";
                if (rdoOfflineZip.Checked)  //If origin was ZIP
                {                  
                    //Prepare New File
                    updateStatus("Creating a copy of original file...");
                    newFilePath = Path.GetDirectoryName(filePath) + "\\PATCHED - " + Path.GetFileName(filePath);
                    writeToLog("Making a copy of original ZIP file to: " + newFilePath);
                    File.Copy(filePath, newFilePath, true);

                    //Patch ZIP
                    writeToLog("Applying patch to new ZIP file");
                    updateStatus("Patching new ZIP file...");
                    zipAction.FastZipSave(newFilePath, toolPath + "framework.jar", fwPath);
                }
                else if (rdoOfflineJar.Checked) // If jar       
                {
                    //Copy back patched file
                    updateStatus("Copying patched file back to original directory...");
                    newFilePath = Path.GetDirectoryName(filePath) + "\\PATCHED - " + Path.GetFileName(filePath);
                    writeToLog("Copying back patched JAR to: " + newFilePath);
                    File.Copy(toolPath + "framework.jar", newFilePath,true);
                }
                else //Should NEVER EVER HAPPEN - fallback
                {
                    throw new Exception("Something went SERIOUSLY wrong!");
                }
                DialogResult dlgYEsNo = MessageBox.Show("File patched successfuly.\nDo you want to view the file now?", "File Patched!", MessageBoxButtons.YesNo);
                if (dlgYEsNo == DialogResult.Yes)
                {
                    string argument = @"/select, " + newFilePath;
                    System.Diagnostics.Process.Start("explorer.exe", argument);
                }
            }
            catch (Exception newException)
            {
                if (newException.Message.Contains("Operation cancelled!"))
                {
                    MessageBox.Show("Operation Cancelled!", "Cancelled!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                else if (newException.Message.Contains("No such file or directory"))
                {
                    MessageBox.Show("File transfer failed because some crucial program files are missing.\n Please re-download the program and make sure to extract everything before trying again.", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    writeToLog("ERROR OCCURED: " + newException.Message);
                    return;
                }
                else if (newException.Message.Contains("File transfer to phone failed!"))
                {
                    MessageBox.Show("File transfer failed because the system is in use.\n It is recommended that you start your phone in Recovery and try again.", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    writeToLog("ERROR OCCURED: " + newException.Message);
                    return;
                }
                else
                {
                    MessageBox.Show("An unexpected error occured and has been logged into the log file: \n" + newException.Message, "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    writeToLog("ERROR OCCURED: " + newException.Message);
                    return;
                }
            }
            finally
            {
                FindAndKillProcess("java.exe");
                enableAfterProcessing();
                clearOfflineForm();
            }                      
        }     
        #endregion

        #endregion

        #region Manual Operations
        
        #region Extract

        #region Extracting Interface
        private void btnManualExtract_Click(object sender, EventArgs e)
        {
            try
            {
                writeToLog("PROTOOLS: Extract Manually");
                //Create operation arguments
                string[] arguments = new string[2];

                //Select Source if not phone
                if (rdoManualExFw.Checked)
                {
                    //Select framework file to process//
                    writeToLog("Select source framework path");
                    OpenFileDialog fwSource = new OpenFileDialog();
                    fwSource.CheckFileExists = true;
                    fwSource.Filter = "framework.jar|*.jar";
                    fwSource.Title = "Select framework.jar file";
                    DialogResult dlgResult = fwSource.ShowDialog();
                    if (dlgResult == System.Windows.Forms.DialogResult.Cancel)
                    {
                        writeToLog("Aborted File Selection!");
                        MessageBox.Show("Operation aborted!", "Operation Aborted!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                        return;
                    }
                    arguments[1] = fwSource.FileName; //Select filename as argument 1
                }
                else if (rdoManualExZip.Checked)
                {
                    //Select framework file to process//
                    writeToLog("Select source ROM Zip");
                    OpenFileDialog zipSource = new OpenFileDialog();
                    zipSource.CheckFileExists = true;
                    zipSource.Filter = "*.zip|*.zip";
                    zipSource.Title = "Select ROM Zip file";
                    DialogResult dlgResult = zipSource.ShowDialog();
                    if (dlgResult == System.Windows.Forms.DialogResult.Cancel)
                    {
                        writeToLog("Aborted File Selection!");
                        MessageBox.Show("Operation aborted!", "Operation Aborted!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                        return;
                    }
                    arguments[1] = zipSource.FileName; //Select ROM Zip as argument 1
                }
                else if (rdoManualExPhone.Checked)
                {
                CHECK:
                    //Making sure phone is connected
                    string isDevice = checkDevice();
                    if (isDevice != "device" && isDevice != "recovery")
                    {
                        DialogResult AbortRetry = MessageBox.Show("Device not found!\nMake sure your device is connected, in debugging mode or recovery, and is recognized by Windows", "Device not found!", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
                        if (AbortRetry == System.Windows.Forms.DialogResult.Retry)
                        {
                            writeToLog("Trying again...");
                            goto CHECK;
                        }
                        else
                        {
                            writeToLog("Operation aborted!");
                            MessageBox.Show("Operation Cancelled!", "Cancelled!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            return;
                        }
                    }
                }

                //Select target
                writeToLog("Selecting target directory");
                FolderBrowserDialog clsTarget = new FolderBrowserDialog();
                clsTarget.ShowNewFolderButton = true;
                clsTarget.Description = "Select location for 'Classes' dir";
                clsTarget.ShowDialog();
                DialogResult overWrite = DialogResult.Yes;
                if (Directory.Exists(clsTarget.SelectedPath + "\\classes\\"))
                {
                    overWrite = MessageBox.Show("Directory 'classes' already exists at selected location.\nDo you wish to overwrite it?", "Directory exists. Overwrite?", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                }
                if (overWrite != DialogResult.Yes || clsTarget.SelectedPath == "")
                {
                    writeToLog("Aborted extraction!");
                    MessageBox.Show("Operation aborted!", "Operation Aborted!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                    return;
                }
                arguments[0] = clsTarget.SelectedPath; //Set selected source as argument 0

                backgroudnExtract.RunWorkerAsync(arguments);
                disableForProcessing(true);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region Extracting Process
        private void backgroudnExtract_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                //Pre Cleanup//
                updateStatus("Preparing...");
                writeToLog("Clearing...");
                preCleanUp(true);

                //Setup - receive arguments //
                string[] incomingArgument = (String[])e.Argument;
                string clsTarget = incomingArgument[0];
                string frameworkPath;

                //Handle sources
                if (incomingArgument[1] != null && rdoManualExFw.Checked) //Meaning a source framework file was transfered
                {
                    frameworkPath = incomingArgument[1]; //Set it as path
                }
                else if (incomingArgument[1] != null && rdoManualExZip.Checked) //Meaning a source ROM Zip was transfered
                {
                    //Extracting framework fROM Zip//
                    string zipPath = incomingArgument[1];
                    writeToLog("Checking for framework.jar in ROM Zip");
                    string fwPath = zipAction.findFile(zipPath, "framework.jar"); //Make sure it has a framework.jar file
                    if (fwPath == null)
                    {
                        writeToLog("ERROR: no framework.jar found in ROM");
                        throw new Exception("Framework.jar not found in file");
                    }
                    writeToLog("Extracting framework fROM Zip");
                    updateStatus("Extracting framework file from ROM Zip...");
                    zipAction.manualZipExtract(zipPath, toolPath, "framework.jar", true); //Extract the file without directories
                    if (File.Exists(toolPath + "framework.jar") == false) //Make sure extraction worked
                    {
                        writeToLog("ERROR: Framework.jar not extracted correctly");
                        throw new Exception("Framework.jar extraction failed");
                    }
                    frameworkPath = toolPath + "framework.jar"; //Set extracted file as source
                }
                else if (incomingArgument[1] == null && rdoManualExPhone.Checked) //Meaning nothing was transfered and file needs to be pulled
                {
                    //Fetch framework file//
                    writeToLog("Pulling framework file");
                    updateStatus("Pulling framework file from phone...");
                    string pullString = ADBShell(@"pull /system/framework/framework.jar """ + toolPath.Replace(@"\", @"/").Substring(0, toolPath.Length - 1) + @"""",0);
                    if (!pullString.Contains("KB/s") || File.Exists(toolPath + "framework.jar") == false) //Did not transfer!
                    {
                        throw new Exception("File transfer failed!\nADB Returned: " + pullString);
                    }
                    frameworkPath = toolPath + "framework.jar"; //Set pulled file as source
                }
                else //Should never ever happen - means nothing was transfered AND 'pull file' was NOT selected
                {
                    throw new Exception("You're fucked..."); //You have paid the price for your lack of vision
                }
                //Final fallback - make sure frameworkpath was set correctly - redundency is serenity.
                if (frameworkPath == null || File.Exists(frameworkPath) == false)
                {
                    throw new Exception("Something went wrong and source path was not set correctly.\nFramework path is: " + frameworkPath);
                }

                //Extract stuff//
                writeToLog("Extracting classes.dex");
                updateStatus("Extracting classes.dex...");
                extractFromJar(frameworkPath, toolPath);
                writeToLog("Extracting classes dir");
                waitWindow.btnCancel.Enabled = false;
                updateStatus("Extracting classes directory (this might take a while)...");
                extractClasses(toolPath + "classes.dex",  clsTarget + "\\classes");
                e.Result = clsTarget;
            }
            catch (Exception ex)
            {
                writeToLog("Extraction failed!\nError was: " + ex.Message);
                e.Result = "failed";
                if (ex.Message.Contains("cannot create")) //No permissions
                {
                    MessageBox.Show("The program failed to write to the directory you specified.\nMake sure you have sufficient writing permissions, and try running the program as Administrator if necessary!","Error!",MessageBoxButtons.OK,MessageBoxIcon.Error);
                    return;
                }
                if (ex.Message.Contains("error: device not found"))
                {
                    writeToLog("Device not conencted!");
                    MessageBox.Show("Device not found!\nMake sure your device is connected, in debugging mode or recovery, and is recognized by Windows.", "Device Not Connected", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                if (ex.Message.Contains("Framework.jar not found in file"))
                {
                    writeToLog("Framework.jar not found in file.");
                    MessageBox.Show("The ROM Zip you have provided does not contain a 'framework.jar' file.\nMake sure it is a valid ROM Zip file!","Invalid ROM Zip File",MessageBoxButtons.OK,MessageBoxIcon.Error);
                    return;
                }
                throw ex;
            }
        } 
        private void backgroudnExtract_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            enableAfterProcessing();
            waitWindow.btnCancel.Enabled = true;
            if (e.Result.ToString() != "failed")
            {
                DialogResult yesno = MessageBox.Show("Operation complete successfully.\n Would you like to view the folder?", "Folder extracted successfully!", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (yesno == System.Windows.Forms.DialogResult.Yes)
                {
                    string filePath = e.Result.ToString();
                    string argument = filePath + "\\classes";
                    System.Diagnostics.Process.Start("explorer.exe", argument);
                }
            }
        }
        #endregion

        #endregion
        
        #region Archive
        
        #region Buttons
        private void btnArchiveManual_Click(object sender, EventArgs e)
        {
            try
            {
                writeToLog("PROTOOLS: Archiving classes directory innitiated!");
                //Make sure a method is selected
                if (rdoManualArcFile.Checked == false && rdoManualArcPhone.Checked == false && rdoManualArcZip.Checked == false) //shouldn't happen, default should be 'show'
                {
                    writeToLog("No method selected");
                    MessageBox.Show("You must select an action to perform with file after archiving", "Archiving action not selected", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                if (rdoManualArcPhone.Checked)
                {
                CHECK:
                    //Making sure phone is connected
                    string isDevice = checkDevice();
                    if (isDevice != "device" && isDevice != "recovery")
                    {
                        DialogResult AbortRetry = MessageBox.Show("Device not found!\nMake sure your device is connected, in debugging mode or recovery, and is recognized by Windows", "Device not found!", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
                        if (AbortRetry == System.Windows.Forms.DialogResult.Retry)
                        {
                            writeToLog("Trying again...");
                            goto CHECK;
                        }
                        else
                        {
                            writeToLog("Operation aborted!");
                            MessageBox.Show("Operation Cancelled!", "Cancelled!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            return;
                        }
                    }
                }
                //Make sure device is connected
                //Explain the process to the user and prompt to contine
                writeToLog("Explaining process...");
                DialogResult MakeSure = MessageBox.Show("This process compresses a modified 'classes' directory into a 'classes.dex' file and then applies it to a 'framework.jar' file.\nFor this process to work, you need to have both a valid 'classes' directory and a 'framework.jar' file for patching.\nDo you understand this process and have both requirements at the ready?\n\nP.S. - Your 'framework.jar' file will be backed up as 'framework.bak' before patching.", "Are you sure you know what you're doing?", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (MakeSure == System.Windows.Forms.DialogResult.No)
                {
                    writeToLog("User aborted");
                    MessageBox.Show("Operation aborted!", "Operation aborted!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                    return;
                }
                
                string[] argument = new string[3];
                argument[0] = selectClassesSource();
                if (argument[0] == null || Directory.Exists(argument[0]) == false) //Make sure folder selection process not aborted or faked
                {
                    writeToLog("User aborted directory selection");
                    return;
                }
                
                //Make sure selected directory is actually a valid 'classes' directory
                if (Directory.Exists(argument[0] + "\\android") == false && Directory.Exists(argument[0] + "\\com") == false && Directory.Exists(argument[0] + "\\javax") == false)
                {
                    writeToLog("Selected directory is not a valid 'classes' directory");
                    MessageBox.Show("The directory you have selected is not a valid 'classes' directory!\nOperation aborted!", "Directory invalid!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                RESELECT: 
                argument[1] = selectFrameworkTarget();
                //Make suree selected Framework.jar is 'valid'
                if (zipAction.findFile(argument[1], "classes.dex") == null) //If the file does not contain classes.dex
                {
                    writeToLog("Selected framework file is not a valid framework.jar file.");
                    DialogResult ARI = MessageBox.Show("The framework file you have selected does not appear to be a proper 'framework.jar' file!\nPlease select a valid one!", "Directory invalid!", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Error);
                    if (ARI == System.Windows.Forms.DialogResult.Abort)
                    {
                        writeToLog("Operation aborted!");
                        MessageBox.Show("Operation aborted!", "Directory invalid!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        return;
                    }
                    else if (ARI == System.Windows.Forms.DialogResult.Retry)
                    {
                        goto RESELECT;
                    }
                    else if (ARI == System.Windows.Forms.DialogResult.Ignore)
                    {
                        writeToLog("Ignored!");
                        DialogResult yesNo = MessageBox.Show("Warning: Trying to patch an uncompatible framework.jar might result in errors and may make your rom unbootable.\nAre you sure you want to continue?", "Warning!", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
                        if (yesNo == System.Windows.Forms.DialogResult.No)
                        {
                            writeToLog("Operation aborted!");
                            MessageBox.Show("Operation aborted!", "Directory invalid!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            return;
                        }
                    }
                }
                if (argument[1] == null || File.Exists(argument[1]) == false) //Make sure file selection process not aborted or faked
                {
                    writeToLog("User aborted file selection");
                    return;
                }
                if (rdoManualArcZip.Checked)
                {
                    //Select ZIP file to archive to//
                    writeToLog("Select target ROM Zip");
                    OpenFileDialog zipTarget = new OpenFileDialog();
                    zipTarget.CheckFileExists = true;
                    zipTarget.Filter = "*.zip|*.zip";
                    zipTarget.Title = "Select ROM Zip file";
                    DialogResult dlgResult = zipTarget.ShowDialog();
                    if (dlgResult == System.Windows.Forms.DialogResult.Cancel)
                    {
                        writeToLog("Aborted File Selection!");
                        MessageBox.Show("Operation aborted!", "Operation Aborted!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                        return;
                    }
                    argument[2] = zipTarget.FileName; //Select ROM Zip as argument 1
                }

                //Start workin'
                backgroundArchive.RunWorkerAsync(argument);
                disableForProcessing(true);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region Archiving Process

        private string selectClassesSource()
        {
            try
            {
                //Select directory to assemble and push
                writeToLog("PROTOOLS: Select classes directory to archive");
                FolderBrowserDialog clsDir = new FolderBrowserDialog();
                clsDir.ShowNewFolderButton = false;
                clsDir.Description = "Select ROM Zip file";
                clsDir.ShowDialog();
                if (clsDir.SelectedPath == "" || Directory.Exists(clsDir.SelectedPath) == false)
                {
                    writeToLog("Aborted File Selection!");
                    MessageBox.Show("Operation aborted!", "Operation Aborted!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                    return null;
                }
                else
                {
                    return clsDir.SelectedPath;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        private string selectFrameworkTarget()
        {
            try
            {
                //Select directory to assemble and push
                writeToLog("PROTOOLS: Select framework.jar to save to");
                OpenFileDialog jarTarget = new OpenFileDialog();
                jarTarget.Title = "Select JAR file to save to";
                jarTarget.Filter = "framework.jar|*.jar";
                jarTarget.CheckFileExists = true;
                DialogResult dlgResult = jarTarget.ShowDialog();
                if (dlgResult == System.Windows.Forms.DialogResult.Cancel)
                {
                    writeToLog("Aborted extraction!");
                    MessageBox.Show("Operation aborted!", "Operation Aborted!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                    return null;
                }
                else
                {
                    return jarTarget.FileName;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private void backgroundArchive_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                //Receive argument strings//
                string[] arguments = (String[])e.Argument;
                string sourcePath = arguments[0];
                string targetFile = arguments[1];
                
                //Preparing//
                updateStatus("Preparing...");
                writeToLog("Starting manual archiving\n.Source path: " + sourcePath + "\nTarget File: " + targetFile);
                writeToLog("Cleaning up work dir...");
                preCleanUp(true);

                //Compressing Classes dir to classes.dex
                writeToLog("Compressing classes dir to classes.dex");
                updateStatus("Compressing 'classes' dir (this might take a while)...");
                waitWindow.btnCancel.Enabled = false;
                compressClasses(sourcePath, toolPath + "classes_new.dex");
                if (File.Exists(toolPath + "classes_new.dex") == false)
                {
                    throw new Exception("classes file failed to create!");
                }

                //Creating a backup of old 'framework.jar'
                writeToLog("Creating backup of old framework file...");
                updateStatus("Creating backup of old framework file...");
                File.Copy(targetFile, Path.GetDirectoryName(targetFile) +"\\"+ Path.GetFileNameWithoutExtension(targetFile) + ".bak",true);

                //Adding classes.dex to framework.jar
                writeToLog("Saving classes.dex to specified framework.jar");
                updateStatus("Patching framework file..");
                zipAction.FastZipSave(targetFile, toolPath + "classes_new.dex", "classes.dex");
                
                //Finally, wat do?
                if (rdoManualArcFile.Checked) //Just show file
                {
                    e.Result = targetFile;
                    return;
                }
                else if (rdoManualArcPhone.Checked) //Patch phone
                {
                    writeToLog("Attempting to write to phone...");
                    updateStatus("Patching phone with newly created framework.jar...");
                    //Inform user you're about to patch phone, and ask if he's ready
                    DialogResult yesno = MessageBox.Show("The program will now attempt to patch your phone.\nPlease make sure it is connected, in debugging mode or recovery and is recognized by the system.\n\nIf your phone is in recovery, make sure to mount the 'System' and 'SD-EXT' (if needed) partitions!", "Folder compressed successfully!", MessageBoxButtons.OKCancel, MessageBoxIcon.Information);
                    if (yesno != System.Windows.Forms.DialogResult.OK) //If user aborted, ask to show file                   
                    {
                        DialogResult aborted = MessageBox.Show("Patching phone aborted!\nWould you like to view the file instead?", "Patching Operation Aborted!", MessageBoxButtons.OKCancel, MessageBoxIcon.Information);
                        if (yesno == System.Windows.Forms.DialogResult.OK)
                        {
                            string argument = @"/select, " + targetFile;
                            System.Diagnostics.Process.Start("explorer.exe", argument);
                        }
                        e.Result = "ignore";
                    }
                    else //Continue patching
                    {
                        string pushString = ADBShell("push " + targetFile + " /system/framework/",0);
                        if (!pushString.Contains("KB/s")) //Did not transfer!
                        {
                            e.Result = "error";
                            throw new Exception("File transfer failed!");
                        }
                        //Ask to reboot
                        DialogResult dlgResult = MessageBox.Show("Patching Operation Complete! \nHowever, before the changes take effect, you MUST reboot your device. \nDo you wish to do so now?", "Operation Complete!", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                        if (dlgResult == DialogResult.Yes)
                        {
                            writeToLog("Sending reboot to device");
                            updateStatus("Rebooting phone...");
                            ADBShell("reboot",0);
                            MessageBox.Show("Your phone has just been rebooted. \nFirst boot after the patching may take longer than usual. This is normal.\nThank you for using the Android RTL Patcher!", "Operation Complete!", MessageBoxButtons.OK, MessageBoxIcon.Information);
                        }
                        e.Result = "ignore";
                        return;
                    }
                }
                else if (rdoManualArcZip.Checked)
                {
                    //Patch Zip//
                    //Prepare paths//
                    string zipPath = arguments[2];
                    if (zipPath == null || File.Exists(zipPath) == false) //Make sure it's right
                    {
                        throw new Exception("Selected ZIP file does not exist or parameter not transfered correctly.\nZip Path: " + zipPath);
                    }
                    //Examine ZIP File
                    writeToLog("Checking target ZIP for framework.jar...");
                    updateStatus("Analyzing target ZIP file...");
                    string frameworkPath = zipAction.findFile(zipPath, "framework.jar");
                    if (frameworkPath == null || frameworkPath == "")
                    {
                        writeToLog("Framework.jar not found in zip file!");
                        DialogResult yesNo = MessageBox.Show("The ROM Zip file you have supplied does not currently contain a framework.jar file.\nDo you want to apply the patch to the default location? (/system/framework/)\n\nWarning, this might not work on all ROMs!", "Framework file not found in ZIP", MessageBoxButtons.YesNo, MessageBoxIcon.Error);
                        if (yesNo == System.Windows.Forms.DialogResult.Yes)
                        {
                            frameworkPath = "system/framework/framework.jar";
                        }
                        else
                        {
                            writeToLog("User chose not to use default!");
                            throw new Exception("Selected ZIP does not contain framework.jar!");
                        }
                    }

                    //Prepare New File
                    updateStatus("Creating a copy of original file...");
                    String newFilePath = Path.GetDirectoryName(zipPath) + "\\PATCHED - " + Path.GetFileName(zipPath);
                    writeToLog("Making a copy of original ZIP file to: " + newFilePath);
                    File.Copy(zipPath, newFilePath, true);
                    
                    //Patch ZIP
                    writeToLog("Applying patch to new ZIP file");
                    updateStatus("Patching new ZIP file...");
                    zipAction.FastZipSave(newFilePath, toolPath + "framework.jar", frameworkPath);
                    e.Result = newFilePath;
                    return;
                }
            }
            catch (Exception ex)
            {
                writeToLog("ERROR!");
                writeToLog(ex.Message);
                MessageBox.Show("An unexpected error occured!\n\n" + ex.Message, "Unexpected Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
        }
        private void backgroundArchive_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            writeToLog("Manual archiving compelte!");
            enableAfterProcessing();
            if ((String)e.Result == "failed" || (String)e.Result == "" || e.Result == null)
            {
                writeToLog("Operation failed, result was void");
                MessageBox.Show("Patching operation failed!", "Operation failed!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            else if ((String)e.Result == "ignore")
            {
                writeToLog("Operation complete, but already handled");
                return;
            }
            else
            {
                writeToLog("Prompting user to see file - if successfull");
                DialogResult yesno = MessageBox.Show("Operation complete successfully.\n Would you like to view the patched file?", "Folder compressed successfully!", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (yesno == System.Windows.Forms.DialogResult.Yes)
                {
                    string argument = @"/select, " + e.Result;
                    System.Diagnostics.Process.Start("explorer.exe", argument);
                }
                return;
            }
        }

        #endregion

        #endregion

        #region Generic Manual Functions
        private void manualPull(string sourceFile, string sourcePath)
        {
            try
            {
                //Pull File
                writeToLog("Pulling " + sourceFile + "manually");
                SaveFileDialog saveFile = new SaveFileDialog();
                saveFile.OverwritePrompt = true;
                saveFile.CheckPathExists = true;
                saveFile.Filter = sourceFile + "|*" + Path.GetExtension(sourceFile);
                saveFile.FileName = sourceFile;
                DialogResult saveFileResult = saveFile.ShowDialog();

                //Handle cancellation//
                if (saveFileResult == System.Windows.Forms.DialogResult.Cancel)
                {
                    writeToLog("User Cancelled!");
                    MessageBox.Show("Operation Cancelled!", "Cancelled!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

            //Fetch file//
            FETCH:
                updateStatus("Please wait...");
                waitWindow.btnCancel.Enabled = false;
                disableForProcessing(false);
                writeToLog("Pulling " + sourcePath + sourceFile + " to " + saveFile.FileName);
                string pullString = ADBShell(@"pull " + sourcePath + sourceFile + @" """ + saveFile.FileName.Replace(@"\", @"/") + @"""",0);
                enableAfterProcessing();
                if (!pullString.Contains("KB/s") || File.Exists(saveFile.FileName) == false) //Did not transfer!
                {
                    if (pullString.Contains("device not found"))
                    {
                        writeToLog("ERROR: Device not found!");
                        DialogResult AbortRetry = MessageBox.Show("Device not found!\nMake sure your device is connected, in debugging mode or recovery, and is recognized by Windows", "Device not found!", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
                        if (AbortRetry == System.Windows.Forms.DialogResult.Retry)
                        {
                            writeToLog("Trying again...");
                            goto FETCH;
                        }
                        else
                        {
                            writeToLog("Operation aborted!");
                            MessageBox.Show("Operation Cancelled!", "Cancelled!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            return;
                        }
                    }
                    else //Device was found but other error
                    {
                        throw new Exception("The file was not pulled successfully! ADB returned: " + pullString);
                    }
                }
                else //Pulled successfuly
                {
                    DialogResult yesno = MessageBox.Show("Operation complete successfully.\n Would you like to view the file?", "File pulled successfully!", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                    if (yesno == System.Windows.Forms.DialogResult.Yes)
                    {
                        string filePath = saveFile.FileName;
                        string argument = @"/select, " + filePath;
                        System.Diagnostics.Process.Start("explorer.exe", argument);
                    }
                }
            }
            catch (Exception ex)
            {

                if (ex.Message.Contains("File transfer failed!"))
                {
                    writeToLog("ERROR: " + ex.Message);
                    MessageBox.Show("An error occured while attempting to pull the file!\nThe error was: " + ex.Message, "An unexpected error occured!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                else
                {
                    writeToLog("ERROR: Transfer Failed! Message was: " + ex.Message);
                    MessageBox.Show("An unexpected error occured!\nThe error was: " + ex.Message, "An unexpected error occured!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
            }
        }
        private void manualPush(string sourceFile, string targetPath)
        {
            try
            {
                writeToLog("Pushing " + sourceFile + " manually!");
                //Push file//
            PUSH:
                updateStatus("Please wait...");
                waitWindow.btnCancel.Enabled = false;
                disableForProcessing(false);
                writeToLog("Pushing " + sourceFile + " to " + targetPath);
                string pushString = ADBShell(@"push """ + sourceFile.Replace(@"\", @"/") + @""" " + targetPath,0);
                enableAfterProcessing();
                if (!pushString.Contains("KB/s")) //Did not transfer!
                {
                    if (pushString.Contains("device not found"))
                    {
                        writeToLog("ERROR: Device not found!");
                        DialogResult AbortRetry = MessageBox.Show("Device not found!\nMake sure your device is connected, in debugging mode or recovery, and is recognized by Windows", "Device not found!", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
                        if (AbortRetry == System.Windows.Forms.DialogResult.Retry)
                        {
                            writeToLog("Trying again...");
                            goto PUSH;
                        }
                        else
                        {
                            writeToLog("Operation aborted!");
                            MessageBox.Show("Operation Cancelled!", "Cancelled!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            return;
                        }
                    }
                    else //Device was found but other error
                    {
                        throw new Exception("The file was not pushed successfully! ADB returned: " + pushString);
                    }
                }
                else //Pushed successfuly
                {
                   MessageBox.Show("Operation complete successfully!", "File pushed successfully!", MessageBoxButtons.OK, MessageBoxIcon.None);
                   writeToLog("Operation Compelte!");
                   return;
                }
            }
            catch (Exception ex)
            {
                enableAfterProcessing();
                if (ex.Message.Contains("Read-only file system")) //Error - not writable
                {
                    writeToLog("ERROR: File could not be written, read only file system!");
                    MessageBox.Show("The file could not be pushed as your system is Read Only!\nMake sure your phone is rooted or boot into recovery!", "Installation Failed!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                if (ex.Message.Contains("File transfer failed!"))
                {
                    writeToLog("ERROR: " + ex.Message);
                    MessageBox.Show("An error occured while attempting to pull the file!\nThe error was: " + ex.Message, "An unexpected error occured!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                else
                {
                    writeToLog("ERROR: Transfer Failed! Message was: " + ex.Message);
                    MessageBox.Show("An unexpected error occured!\nThe error was: " + ex.Message, "An unexpected error occured!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
            }
        }
        #endregion 

        #region Pull
        private void btnFrameworkPull_Click(object sender, EventArgs e)
        {
            manualPull("framework.jar","/system/framework/");
        }    
        private void btnPullFWRes_Click(object sender, EventArgs e)
        {
            manualPull("framework-res.apk","/system/framework/");
        }
        private void btnPullBP_Click(object sender, EventArgs e)
        {
            manualPull("build.prop", "/system/");
        }
        private void btnPullManual_Click(object sender, EventArgs e)
        {
            try
            {
                //Querying user for path to pull from               
            QUERYPATH:
                pathQuery.ShowDialog();
                if (pathQuery.DialogResult != System.Windows.Forms.DialogResult.OK)
                {
                    writeToLog("Operation aborted!");
                    MessageBox.Show("Operation Cancelled!", "Cancelled!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                string remotePath = pathQuery.txtPath.Text.Trim();
                
                //Checking that path exists
            VERIFY:
                string testRemotePath = ADBShell("shell ls " + remotePath,0);
                if (testRemotePath.Contains("device not found")) //Device not found
                {
                    writeToLog("ERROR: Device not found!");
                    DialogResult AbortRetry = MessageBox.Show("Device not found!\nMake sure your device is connected, in debugging mode or recovery, and is recognized by Windows", "Device not found!", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
                    if (AbortRetry == System.Windows.Forms.DialogResult.Retry)
                    {
                        writeToLog("Trying again...");
                        goto VERIFY;
                    }
                    else
                    {
                        writeToLog("Operation aborted!");
                        MessageBox.Show("Operation Cancelled!", "Cancelled!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        return;
                    }
                }
                else if (testRemotePath.Contains("No such file or directory")) //Not a valid path!
                {
                    writeToLog(remotePath + " is not a valid path!");
                    DialogResult YesNo = MessageBox.Show("The path you have selected is not valid or does not exist!\nDo you want to enter a different path?\n\nYour paths should be in full format, e.g. /system/framework/framework.jar", "Path Not Valid!", MessageBoxButtons.YesNo, MessageBoxIcon.Error);
                    if (YesNo == System.Windows.Forms.DialogResult.Yes)
                    {
                        goto QUERYPATH;
                    }
                    else
                    {
                        writeToLog("Operation aborted!");
                        MessageBox.Show("Operation Cancelled!", "Cancelled!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        return;
                    }
                }
                else //Path is valid
                {
                    FolderBrowserDialog whereToSave = new FolderBrowserDialog();
                    whereToSave.Description = "Where to save your pulled file/folder?";
                    whereToSave.ShowNewFolderButton = true;
                    DialogResult WTSResult = whereToSave.ShowDialog();
                    //Handle cancellation//
                    if (WTSResult == System.Windows.Forms.DialogResult.Cancel)
                    {
                        writeToLog("User Cancelled!");
                        MessageBox.Show("Operation Cancelled!", "Cancelled!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        return;
                    }

            //Fetch file//
                FETCH:
                    updateStatus("Please wait...");
                    waitWindow.btnCancel.Enabled = false;
                    disableForProcessing(false);
                    writeToLog("Pulling " + remotePath + " to " + whereToSave.SelectedPath);
                    string pullString = ADBShell(@"pull " + remotePath + @" """ + whereToSave.SelectedPath.Replace(@"\", @"/") + @"""",0);
                    enableAfterProcessing();
                    if (!pullString.Contains("KB/s") && !pullString.Contains("->")) //Did not transfer!
                    {
                        if (pullString.Contains("device not found"))
                        {
                            writeToLog("ERROR: Device not found!");
                            DialogResult AbortRetry = MessageBox.Show("Device not found!\nMake sure your device is connected, in debugging mode or recovery, and is recognized by Windows", "Device not found!", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
                            if (AbortRetry == System.Windows.Forms.DialogResult.Retry)
                            {
                                writeToLog("Trying again...");
                                goto FETCH;
                            }
                            else
                            {
                                writeToLog("Operation aborted!");
                                MessageBox.Show("Operation Cancelled!", "Cancelled!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                                return;
                            }
                        }
                        else //Device was found but other error
                        {
                            throw new Exception("The file was not pulled successfully! ADB returned: " + pullString);
                        }
                    }
                    else //Pulled successfuly
                    {
                        DialogResult yesno = MessageBox.Show("Operation complete successfully.\n Would you like to view the pulled file/folder?", "File pulled successfully!", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                        if (yesno == System.Windows.Forms.DialogResult.Yes)
                        {
                            string filePath = whereToSave.SelectedPath;
                            string argument = @"/select, " + filePath;
                            System.Diagnostics.Process.Start("explorer.exe", argument);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region Push
        private void btnPushFw_Click(object sender, EventArgs e)
        {
            try
            {
                writeToLog("PROTOOLS: Push framework.jar manually!");

                //Checking device state
                string isDevice = checkDevice();
                if (isDevice != "recovery" && isDevice != "device")
                {
                    writeToLog("Device not detected or detection error! Device detection returned: " + isDevice);
                    MessageBox.Show("Device not detected or detection error\nMake sure your device is connected, in recovery or USB debugging mode, and is recognized by Windows!", "Detection error!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                    return;
                }

                writeToLog("Select source framework path");
                OpenFileDialog fwSource = new OpenFileDialog();
                fwSource.CheckFileExists = true;
                fwSource.Filter = "framework.jar|*.jar";
                fwSource.Title = "Select framework.jar file";
                DialogResult selectFile = fwSource.ShowDialog();
                if (selectFile == System.Windows.Forms.DialogResult.Cancel)
                {
                    writeToLog("Aborted File Selection!");
                    MessageBox.Show("Operation aborted!", "Operation Aborted!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                    return;
                }
                manualPush(fwSource.FileName, "/system/framework/framework.jar");
                DialogResult dlgResult = MessageBox.Show("It is highly recommended that you reboot your device after pushing framework.jar.\nDo you wish to do so now?", "Operation complete successfuly!", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (dlgResult == DialogResult.Yes)
                {
                    writeToLog("Sending reboot to device");
                    updateStatus("Rebooting phone...");
                    ADBShell("reboot",0);
                    MessageBox.Show("Your phone has just been rebooted. \nFirst boot after the patching may take longer than usual. This is normal.\nThank you for using the Android RTL Patcher!", "Operation Complete!", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
            catch (Exception ex)
            {
                writeToLog("ERROR: Unexpected error occured! Message was: " + ex.Message);
                throw ex;
            }
        }
        private void btnPushFwRes_Click(object sender, EventArgs e)
        {
            {
                try
                {
                    writeToLog("PROTOOLS: Push framework.jar manually!");
                    //Checking device state
                    string isDevice = checkDevice();
                    if (isDevice == "device")
                    {
                        DialogResult yesNo = MessageBox.Show("Pushing a 'framework-res.apk' file to the phone while it's not in recovery will cause the phone's system to crash.\nAre you sure you wish to continue?", "Warning!", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
                        if (yesNo != System.Windows.Forms.DialogResult.Yes)
                        {
                            writeToLog("Aborted pushing file due to device not in recovery!");
                            MessageBox.Show("Operation aborted!", "Operation Aborted!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                            return;
                        }
                        else
                        {
                            writeToLog("User has been warned, continued anyway!");
                        }
                    }
                    else if (isDevice != "recovery")
                    {
                        writeToLog("Device not detected or detection error! Device detection returned: " + isDevice);
                        MessageBox.Show("Device not detected or detection error\nMake sure your device is connected, in recovery or USB debugging mode, and is recognized by Windows!", "Detection error!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                        return;
                    }
                    writeToLog("Select source framework path");
                    OpenFileDialog fwSource = new OpenFileDialog();
                    fwSource.CheckFileExists = true;
                    fwSource.Filter = "framework-res.apk|*.apk";
                    fwSource.Title = "Select framework-res.apk file";
                    DialogResult selectFile = fwSource.ShowDialog();
                    if (selectFile == System.Windows.Forms.DialogResult.Cancel)
                    {
                        writeToLog("Aborted File Selection!");
                        MessageBox.Show("Operation aborted!", "Operation Aborted!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                        return;
                    }
                    manualPush(fwSource.FileName, "/system/framework/framework-res.apk");
                }
                catch (Exception ex)
                {
                    writeToLog("ERROR: Unexpected error occured! Message was: " + ex.Message);
                    throw ex;
                }
            }
        }
        private void btnPushManual_Click(object sender, EventArgs e)
        {
            try
            {
                writeToLog("PROTOOLS: Push a pre-selected file manually");
                //Checking device state
                string isDevice = checkDevice();
                if (isDevice != "recovery" && isDevice != "device")
                {
                    writeToLog("Device not detected or detection error! Device detection returned: " + isDevice);
                    MessageBox.Show("Device not detected or detection error\nMake sure your device is connected, in recovery or USB debugging mode, and is recognized by Windows!", "Detection error!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                    return;
                }

                //Warn user
                DialogResult yesNo = MessageBox.Show("Due to the nature of this proceedure, it is hard to automatically detect bad paths. If you manually input a wrong path, it WILL be created on the device.\nTherefor, this proceedure is considered highly risky.\nDo you wish to continue?", "Warning!", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
                if (yesNo != System.Windows.Forms.DialogResult.Yes)
                {
                    writeToLog("Aborted pushing file!");
                    MessageBox.Show("Operation aborted!", "Operation Aborted!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                    return;
                }
                else
                {
                    writeToLog("User has been warned, continued anyway!");
                }

                //Select File
                OpenFileDialog selectFile = new OpenFileDialog();
                selectFile.CheckFileExists = true;
                DialogResult openFileResult = selectFile.ShowDialog();

                //Handle cancellation//
                if (openFileResult == System.Windows.Forms.DialogResult.Cancel)
                {
                    writeToLog("User Cancelled!");
                    MessageBox.Show("Operation Cancelled!", "Cancelled!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                //Querying user for path to push to

                pathQuery.ShowDialog();
                if (pathQuery.DialogResult != System.Windows.Forms.DialogResult.OK)
                {
                    writeToLog("Operation aborted!");
                    MessageBox.Show("Operation Cancelled!", "Cancelled!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                string remotePath = pathQuery.txtPath.Text.Trim();
                if (remotePath.Substring(remotePath.Length - 1, 1) == "/") { remotePath = remotePath.Substring(0, remotePath.Length - 1); } //Fixes paths that end in "/" 
                string sourceFile = selectFile.FileName;
                manualPush(sourceFile, remotePath);
            }
            catch (Exception ex)
            {
                writeToLog("ERROR: Unexpected error occured! Error was: " + ex.Message);
                throw ex;
            }
        }
        private void btnPushDirectory_Click(object sender, EventArgs e)
        {
            try
            {
                writeToLog("PROTOOLS: Push a pre-selected directory manually");
                //Checking device state
                string isDevice = checkDevice();
                if (isDevice != "recovery" && isDevice != "device")
                {
                    writeToLog("Device not detected or detection error! Device detection returned: " + isDevice);
                    MessageBox.Show("Device not detected or detection error\nMake sure your device is connected, in recovery or USB debugging mode, and is recognized by Windows!", "Detection error!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                    return;
                }

                //Warn user
                DialogResult yesNo = MessageBox.Show("Due to the nature of this proceedure, it is hard to automatically detect bad paths. If you manually input a wrong path, it WILL be created on the device.\nTherefor, this proceedure is considered highly risky.\nDo you wish to continue?", "Warning!", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
                if (yesNo != System.Windows.Forms.DialogResult.Yes)
                {
                    writeToLog("Aborted pushing file!");
                    MessageBox.Show("Operation aborted!", "Operation Aborted!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                    return;
                }
                else
                {
                    writeToLog("User has been warned, continued anyway!");
                }

                //Select Path
                FolderBrowserDialog selectPath = new FolderBrowserDialog();
                selectPath.ShowNewFolderButton = false;
                selectPath.Description = "Select folder to push";
                DialogResult openFileResult = selectPath.ShowDialog();

                //Handle cancellation//
                if (openFileResult == System.Windows.Forms.DialogResult.Cancel)
                {
                    writeToLog("User Cancelled!");
                    MessageBox.Show("Operation Cancelled!", "Cancelled!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                //Querying user for path to push to

                pathQuery.ShowDialog();
                if (pathQuery.DialogResult != System.Windows.Forms.DialogResult.OK)
                {
                    writeToLog("Operation aborted!");
                    MessageBox.Show("Operation Cancelled!", "Cancelled!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                string remotePath = pathQuery.txtPath.Text.Trim();
                string sourceFile = selectPath.SelectedPath;
                manualPush(sourceFile, remotePath);
            }
            catch (Exception ex)
            {
                writeToLog("ERROR: Unexpected error occured. Message was: " + ex.Message);
                throw ex;
            }
        }
        #endregion

        #region Keyboard

        private void btnKbPhone_Click(object sender, EventArgs e)
        {
            try
            {
                writeToLog("PROTOOLS: Instal keyboard to Phone");
                disableForProcessing(false);
                KeyboardInstall();
                enableAfterProcessing();
                MessageBox.Show("Operation complete successfuly!", "Operation complete successfully!", MessageBoxButtons.OK, MessageBoxIcon.None);
                return;
            }
            catch (Exception ex)
            {
                enableAfterProcessing();
                writeToLog("ERROR: An unexpected error occured! Message was: " + ex.Message);
                throw ex;
            }
        }

        private void btnKbZip_Click(object sender, EventArgs e)
        {
            try
            {
                writeToLog("PROTOOLS: Patch keyboard to ROM Zip");
                //Select ZIP file to patch//
                writeToLog("Select target ROM Zip");
                OpenFileDialog zipTarget = new OpenFileDialog();
                zipTarget.CheckFileExists = true;
                zipTarget.Filter = "*.zip|*.zip";
                zipTarget.Title = "Select ROM Zip file";
                DialogResult dlgResult = zipTarget.ShowDialog();
                if (dlgResult == System.Windows.Forms.DialogResult.Cancel)
                {
                    writeToLog("Aborted File Selection!");
                    MessageBox.Show("Operation aborted!", "Operation Aborted!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                    return;
                }
                string zipPath = zipTarget.FileName; //Select ROM Zip as argument 1

                //Validated Zip//
                if (zipPath == null || File.Exists(zipPath) == false) //Make sure it's right
                {
                    throw new Exception("Selected ZIP file does not exist or parameter not transfered correctly.\nZip Path: " + zipPath);
                }

                //Prepare New File
                disableForProcessing(false);
                updateStatus("Creating a copy of original file...");
                String newFilePath = Path.GetDirectoryName(zipPath) + "\\PATCHED - " + Path.GetFileName(zipPath);
                writeToLog("Making a copy of original ZIP file to: " + newFilePath);
                File.Copy(zipPath, newFilePath, true);

                //Patch ZIP
                writeToLog("Applying patch to new ZIP file");
                updateStatus("Patching ROM Zip...");
                zipAction.FastZipSave(newFilePath, toolPath + "GBKB\\app\\LatinIME.apk", "\\system\\app\\LatimIME.apk");
                zipAction.FastZipSave(newFilePath, toolPath + "GBKB\\lib\\libjni_latinime.so", "\\system\\lib\\libjni_latinime.so");
                enableAfterProcessing();
                DialogResult yesno = MessageBox.Show("Operation complete successfully.\n Would you like to view the file?", "Keyboard installed!", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (yesno == System.Windows.Forms.DialogResult.Yes)
                {
                    string argument = @"/select, " + newFilePath;
                    System.Diagnostics.Process.Start("explorer.exe", argument);
                }
            }
            catch (Exception ex)
            {
                writeToLog("ERROR: Unexpected error! Message was: " + ex.Message);
                throw ex;
            }
        }
        #endregion

        #endregion

        #endregion

        #region libpatcher
        public bool libPatcherMain() //returns wehether patching was successful or not
        {
            try
            {
                //Start by verifying the files exist
                if (!File.Exists(toolPath + "libwebcore.so") || !File.Exists(toolPath + "libandroid_runtime.so"))
                {
                    throw new Exception("missing lib files");
                }
                //Make sure app is using admin privilages



                writeToLog("Calling libPatcher");
                System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo(toolPath + "libpatcher.exe");
                procStartInfo.RedirectStandardOutput = true;
                procStartInfo.RedirectStandardError = true;
                procStartInfo.UseShellExecute = false;
                procStartInfo.CreateNoWindow = true;
                procStartInfo.WorkingDirectory = toolPath;
                System.Diagnostics.Process proc = new System.Diagnostics.Process();
                proc.StartInfo = procStartInfo;
                proc.Start();
                // Get the output into a string
                proc.WaitForExit();
                string result = proc.StandardOutput.ReadToEnd();
                result = result + "\n" + proc.StandardError.ReadToEnd(); //Also catch error just in case
                writeToLog("LibPatcher Returned: \n" + result);

                //Make sure both files were patched.
                bool runtimePatched, webcorePatched = false;
                if (result.Contains("couldn't pach the file libandroid_runtime.so"))
                {
                    runtimePatched = false;
                }
                else if (result.Contains("libandroid_runtime.so patched"))
                {
                    runtimePatched = true;
                }
                else
                {
                    throw new Exception("Unexpected libpatcher output. Output was: " + result);
                }
                if (result.Contains("couldn't pach the file libwebcore_fixed.so"))
                {
                    webcorePatched = false;
                }
                else if (result.Contains("Libwebcore.so patched"))
                {
                    webcorePatched = true;
                }
                if (webcorePatched && runtimePatched && File.Exists(toolPath + "libandroid_runtime_fixed.so") && File.Exists(toolPath + "libwebcore_fixed.so"))
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception ex)
            {
                writeToLog("ERROR: LibPatcher error! Error was: " + ex.Message);
                return false;
            }
        }
        #endregion

        private void button1_Click(object sender, EventArgs e)
        {
            //DEBUGGGING ONLY!!!
            preCleanUp(true);
            bool yomama = libPatcherMain();
            if (yomama) { MessageBox.Show("Ok!"); }
            else if (!yomama) { MessageBox.Show("Not ok!"); }
        }

        private void chkLibPatcher_CheckedChanged(object sender, EventArgs e)
        {
            if (chkLibPatcher.Checked == true)
            {
                MessageBox.Show("Warning! This procedure is experimental and in some cases may cause your phone not to boot.\nPlease back up your phone before using it!", "Warning!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
            }
        }


    }


    #region Custom Classes
    //public class buildProperty //Individual properties of a build
    //{
    //    public string Name { get; set; }
    //    public string Value { get; set; }
    //}
    public class buildProperties //Individuals properties of a build or rom
    {
        public string BuildName { get; set; }
        public string AndroidVersion { get; set; }
        public bool Compatible { get; set; }
        public string BuildBase { get; set; }
        public string SenseVersion { get; set; }
    }
    #endregion 
}
